package org.fast.develop.logicflow.runtime;

import org.fast.develop.logicflow.core.meta.base.Element;
import org.fast.develop.logicflow.core.meta.base.Ref;
import org.fast.develop.logicflow.core.meta.base.line.Incoming;
import org.fast.develop.logicflow.core.meta.base.line.Line;
import org.fast.develop.logicflow.core.meta.base.line.Outgoing;
import org.fast.develop.logicflow.core.meta.base.Resource;
import org.fast.develop.logicflow.core.meta.shape.control.IfElseControl;
import org.fast.develop.logicflow.core.meta.shape.line.SequenceFlow;
import org.fast.develop.logicflow.runtime.node.LineNode;
import org.fast.develop.logicflow.runtime.node.LogicFlowNode;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 莫寅
 * @createTime 2022-11-27 21:59
 * @desc：
 */
public class LogicFlowNodeParser {

    private LogicFlowInstanceParser instanceParser;

    private LogicFlowNodeParser currentGatewayNodeParser;

    private LogicFlowNode logicFlowNode;

    public LogicFlowNodeParser(LogicFlowInstanceParser instanceParser) {
        this.instanceParser = instanceParser;
    }

    public LogicFlowNodeParser(LogicFlowInstanceParser instanceParser, LogicFlowNodeParser currentGatewayNodeParser) {
        this.instanceParser = instanceParser;
        this.currentGatewayNodeParser = currentGatewayNodeParser;
    }

    public LogicFlowNode getLogicFlowNode(LogicFlowNodeParser gatewayNodeParser, Resource resource) {

        if(isEndNode(gatewayNodeParser, resource)){
            return null;
        }

        if(currentGatewayNodeParser != null){
            return new LogicFlowNodeParser(instanceParser, currentGatewayNodeParser).getLogicFlowNode(resource);
        }else if(logicFlowNode.getElement() instanceof IfElseControl){
            return new LogicFlowNodeParser(instanceParser, gatewayNodeParser).getLogicFlowNode(resource);
        }else{
            return new LogicFlowNodeParser(instanceParser).getLogicFlowNode(resource);
        }
    }

    private boolean isEndNode(LogicFlowNodeParser gatewayNodeParser, Element resource){

        if(currentGatewayNodeParser != null && resource instanceof SequenceFlow){
            SequenceFlow sequenceFlow = (SequenceFlow)resource;
            if(sequenceFlow.getCondition() != null){
                return true;
            }
        }

        return false;
    }

    public LogicFlowNode getLogicFlowNode(Resource resource){

        logicFlowNode = createLogicFlowNode(resource);

        LogicFlowNode logicFlowNodeCache = instanceParser.getCacheLogicFLowNodes().get(resource.getResourceId());
        if(logicFlowNodeCache != null){
            return logicFlowNodeCache;
        }else{
            instanceParser.getCacheLogicFLowNodes().put(resource.getResourceId(), logicFlowNode);
        }

        logicFlowNode.setElement(resource);

        if(resource instanceof Incoming){
            Incoming incoming = (Incoming)resource;
            List<LogicFlowNode> incomingLogicFlowNodes = incoming.getIncoming().stream().map(incomingRef -> {
                return getLogicFlowNode(this, getElementByRef(incomingRef));
            }).filter(logicFlowNode1 -> logicFlowNode1 != null)
                    .collect(Collectors.toList());

            logicFlowNode.setPrevNodes(incomingLogicFlowNodes);
        }


        if(resource instanceof Outgoing){
            Outgoing outgoing = (Outgoing)resource;
            List<LogicFlowNode> outgoingResourceLogicFlowNodes = outgoing.getOutgoing().stream().map(outgoingRef -> {
                return getLogicFlowNode(this, getElementByRef(outgoingRef));
            }).filter(logicFlowNode1 -> logicFlowNode1 != null).collect(Collectors.toList());
            logicFlowNode.setNextNodes(outgoingResourceLogicFlowNodes);
        }


//        if(resource instanceof Ref){
//            Ref ref = (Ref)resource;
//            String resourceId = ref.getResourceId();
//            Resource element = instanceParser.getElements().get(resourceId);
//            logicFlowNode = getLogicFlowNode(this, element);
//        }

        if(resource instanceof Line){
            Line line = (Line)resource;
            Ref sourceRef = line.getSourceRef();
            LogicFlowNode sourceRefLogicFlowNode = getLogicFlowNode(this, getElementByRef(sourceRef));
            logicFlowNode.setPrevNodes(Arrays.asList(sourceRefLogicFlowNode));

            Ref targetRef = line.getTargetRef();
            LogicFlowNode targetRefLogicFlowNode = getLogicFlowNode(this, getElementByRef(targetRef));
            logicFlowNode.setNextNodes(Arrays.asList(targetRefLogicFlowNode));

            LineNode lineNode = (LineNode)logicFlowNode;
            lineNode.setExpression(line.getCondition());
            lineNode.setDefault(((Line) resource).getDefault());
        }


        return logicFlowNode;
    }


    private LogicFlowNode createLogicFlowNode(Element element){
        if(element instanceof SequenceFlow){
            return new LineNode();
        }else{
            return new LogicFlowNode();
        }

    }

    private Resource getElementByRef(Ref ref){
        return instanceParser.getElements().get(ref.getResourceId());
    }
}
